﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SQLite;

namespace KeyboardImageViewer
{
    public class ImageQuery
    {
        private bool filtersChanged = true;

        private bool untaggedOnly;

        private string search = "";
        private bool searchFileName;
        private bool searchTags;

        private int minRating;

        private string customClause = "";

        private SortType sort = SortType.Random;
        private bool ascending;

        private string listName = "";

        public bool UntaggedOnly
        {
            get { return this.untaggedOnly; }
            set
            {
                if (value != this.untaggedOnly)
                {
                    this.untaggedOnly = value;
                    this.MarkChanged();
                }
            }
        }

        public string Search
        {
            get { return this.search; }
            set
            {
                if (value.ToLowerInvariant() != this.search)
                {
                    this.search = value.ToLowerInvariant();
                    this.MarkChanged();
                }
            }
        }

        public bool SearchFileName
        {
            get { return this.searchFileName; }
            set
            {
                if (value != this.searchFileName)
                {
                    this.searchFileName = value;
                    this.MarkChanged();
                }
            }
        }

        public bool SearchTags
        {
            get { return this.searchTags; }
            set
            {
                if (value != this.searchTags)
                {
                    this.searchTags = value;
                    this.MarkChanged();
                }
            }
        }

        public int MinRating
        {
            get { return this.minRating; }
            set
            {
                if (value != minRating)
                {
                    this.minRating = value;
                    this.MarkChanged();
                }
            }
        }

        public string CustomClause
        {
            get { return this.customClause; }
            set
            {
                if (value != this.customClause)
                {
                    this.customClause = value;
                    this.MarkChanged();
                }
            }
        }

        public SortType Sort
        {
            get { return this.sort; }
            set
            {
                if (value != this.sort)
                {
                    this.sort = value;
                    this.MarkChanged();
                }
            }
        }

        public bool Ascending
        {
            get { return this.ascending; }
            set
            {
                if (value != this.ascending)
                {
                    this.ascending = value;
                    this.MarkChanged();
                }
            }
        }

        public string ListName
        {
            get { return this.listName; }
            set { this.listName = value; }
        }

        public List<int> GetImageList(SQLiteConnection connection, bool forceRefresh)
        {
            if (forceRefresh)
            {
                this.filtersChanged = true;
            }

            return this.GetImageList(connection);
        }

        public List<int> GetImageList(SQLiteConnection connection)
        {
            if (!this.filtersChanged)
            {
                return null;
            }

            var imageList = new List<int>();

            SQLiteCommand selectCommand = new SQLiteCommand(this.CreateSelectStatement(), connection);
            using (SQLiteDataReader reader = selectCommand.ExecuteReader())
            {
                while (reader.Read())
                {
                    imageList.Add(reader.GetInt32("id"));
                }
            }

            return imageList;
        }

        private string CreateSelectStatement()
        {
            StringBuilder builder = new StringBuilder();
            builder.Append("SELECT id FROM images WHERE included");

            var clauses = new List<string>();

            if (this.search != "" && (this.searchFileName || this.searchTags))
            {
                string[] searchTerms = search.Split(new char[] { ' ' });

                foreach (string rawSearchTerm in searchTerms)
                {
                    bool exclude = false;

                    string searchTerm = rawSearchTerm.Trim();
                    searchTerm = searchTerm.Replace("'", "");
                    searchTerm = searchTerm.Replace("|", "");

                    if (searchTerm != "" && searchTerm[0] == '-')
                    {
                        exclude = true;
                        searchTerm = searchTerm.Substring(1, searchTerm.Length - 1);
                    }

                    if (searchTerm == "")
                    {
                        continue;
                    }

                    if (!exclude)
                    {
                        if (this.searchFileName && this.searchTags)
                        {
                            clauses.Add("(tags LIKE '%|" + searchTerm + "|%' OR dependentTags LIKE '%|" + searchTerm + "|%' OR name LIKE '%" + searchTerm + "%')");
                        }
                        else if (this.searchFileName)
                        {
                            clauses.Add("name LIKE '%" + searchTerm + "%'");
                        }
                        else if (this.searchTags)
                        {
                            clauses.Add("(tags LIKE '%|" + searchTerm + "|%' OR dependentTags LIKE '%|" + searchTerm + "|%')");
                        }
                    }
                    else
                    {
                        if (this.searchFileName && this.searchTags)
                        {
                            clauses.Add("tags NOT LIKE '%|" + searchTerm + "|%' AND dependentTags NOT LIKE '%|" + searchTerm + "|%' AND name NOT LIKE '%" + searchTerm + "%'");
                        }
                        else if (this.searchFileName)
                        {
                            clauses.Add("name NOT LIKE '%" + searchTerm + "%'");
                        }
                        else if (this.searchTags)
                        {
                            clauses.Add("tags NOT LIKE '%|" + searchTerm + "|%' AND dependentTags NOT LIKE '%|" + searchTerm + "|%'");
                        }
                    }
                }
            }

            if (this.untaggedOnly)
            {
                clauses.Add("(tags ISNULL OR tags = '')");
            }

            if (this.minRating != 0)
            {
                if (this.minRating == -1)
                {
                    clauses.Add("(rating ISNULL OR rating = 0)");
                }
                else
                {
                    clauses.Add("rating >= " + this.minRating);
                }
            }

            if (this.customClause != "")
            {
                clauses.Add(this.customClause);
            }

            if (clauses.Count > 0)
            {
                foreach (string clause in clauses)
                {
                    builder.Append(" AND ");
                    builder.Append(clause);
                }
            }

            string orderClause = "";

            string ascString;

            if (this.ascending)
            {
                ascString = "ASC";
            }
            else
            {
                ascString = "DESC";
            }

            switch (this.sort)
            {
                case SortType.Random:
                    orderClause = " ORDER BY RANDOM()";
                    break;
                case SortType.Modified:
                    orderClause = " ORDER BY lastWriteTime " + ascString;
                    break;
                case SortType.Name:
                    orderClause = " ORDER BY name " + ascString;
                    break;
                default:
                    break;
            }

            builder.Append(orderClause);

            return builder.ToString();
        }

        private void MarkChanged()
        {
            this.filtersChanged = true;
            this.listName = "";
        }
    }
}
